package slxy.jiangtao.dts.core.common.dao.impl;

import slxy.jiangtao.dts.core.common.dao.IGenericBaseCommonDao;
import slxy.jiangtao.dts.core.common.hibernate.qbc.CriteriaQuery;
import slxy.jiangtao.dts.core.common.hibernate.qbc.HqlQuery;
import slxy.jiangtao.dts.core.common.hibernate.qbc.Page;
import slxy.jiangtao.dts.core.common.hibernate.qbc.PagerUtil;
import slxy.jiangtao.dts.core.util.OConvertUtils;
import slxy.jiangtao.dts.core.util.StringUtil;
import org.hibernate.*;
import org.hibernate.criterion.*;
import org.hibernate.internal.CriteriaImpl;
import org.hibernate.metadata.ClassMetadata;
import org.hibernate.transform.Transformers;
import org.hibernate.type.Type;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.dao.support.DataAccessUtils;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.util.Assert;

import java.io.Serializable;
import java.util.*;

@SuppressWarnings("hiding")
public abstract class GenericBaseCommonDao<T, PK extends Serializable>
        implements IGenericBaseCommonDao {

    private static final Logger logger = LoggerFactory.getLogger(GenericBaseCommonDao.class);
    /**
     * 注入一个sessionFactory属性,并注入到父类(HibernateDaoSupport)
     */
    @Autowired
    @Qualifier("sessionFactory")
    private SessionFactory sessionFactory;

    public Session getSession() {
        // 事务必须是开启的(Required)，否则获取不到
        return sessionFactory.getCurrentSession();
    }

    /**
     * 获得该类的属性和类型
     *
     * @param entityName 注解的实体类
     */
    private <T> void getProperty(Class entityName) {
        ClassMetadata cm = sessionFactory.getClassMetadata(entityName);
        String[] str = cm.getPropertyNames(); // 获得该类所有的属性名称
        for (int i = 0; i < str.length; i++) {
            String property = str[i];
            String type = cm.getPropertyType(property).getName(); // 获得该名称的类型
            logger.info(property + "---&gt;" + type);
        }
    }

    /**
     * 根据实体名字获取唯一记录
     *
     * @param propertyName
     * @param value
     * @return
     */
    public <T> T findUniqueByProperty(Class<T> entityClass, String propertyName, Object value) {
        Assert.hasText(propertyName);
        return (T) createCriteria(entityClass, Restrictions.eq(propertyName, value)).uniqueResult();
    }

    /**
     * 按属性查找对象列表.
     */
    public <T> List<T> findByProperty(Class<T> entityClass, String propertyName, Object value) {
        Assert.hasText(propertyName);
        return (List<T>) createCriteria(entityClass, Restrictions.eq(propertyName, value)).list();
    }

    /**
     * 根据传入的实体持久化对象
     */
    public <T> Serializable save(T entity) {
        try {
            Serializable id = getSession().save(entity);
            //getSession().flush();
            if (logger.isDebugEnabled()) {
                logger.debug("保存实体成功," + entity.getClass().getName());
            }
            return id;
        } catch (RuntimeException e) {
            logger.error("保存实体异常", e);
            throw e;
        }
    }

    /**实体托管化*/
    public <T> void evict(T entity){
        try {
            getSession().evict(entity);
        }catch (RuntimeException e){
            logger.error("托管实体异常", e);
            throw e;
        }
    }

    /**
     * 批量保存数据
     *
     * @param <T>
     * @param entitys 要持久化的临时实体对象集合
     */
    public <T> void batchSave(List<T> entitys) {
        for (int i = 0; i < entitys.size(); i++) {
            getSession().save(entitys.get(i));
            if (i % 1000 == 0) {
                // 1000个对象批量写入数据库，后才清理缓存
                getSession().flush();
                getSession().clear();
            }
        }
        //最后页面的数据，进行提交手工清理
        getSession().flush();
        getSession().clear();
    }

    /**
     * 根据传入的实体添加或更新对象
     *
     * @param <T>
     * @param entity
     */
    public <T> void saveOrUpdate(T entity) {
        try {
            getSession().saveOrUpdate(entity);
            //getSession().flush();
            if (logger.isDebugEnabled()) {
                logger.debug("添加或更新成功," + entity.getClass().getName());
            }
        } catch (RuntimeException e) {
            logger.error("添加或更新异常", e);
            throw e;
        }
    }

    /**
     * 根据传入的实体删除对象
     */
    public <T> void delete(T entity) {
        try {
            getSession().delete(entity);
            //getSession().flush();
            if (logger.isDebugEnabled()) {
                logger.debug("删除成功," + entity.getClass().getName());
            }
        } catch (RuntimeException e) {
            logger.error("删除异常", e);
            throw e;
        }
    }

    /**
     * 根据主键删除指定的实体
     *
     * @param entityName
     * @param id
     * @param <T>
     */
    public <T> void deleteEntityById(Class entityName, Serializable id) {
        delete(get(entityName, id));
        //getSession().flush();
    }

    /**
     * 删除全部的实体
     *
     * @param <T>
     * @param entitys
     */
    public <T> void deleteAllEntitie(Collection<T> entitys) {
        for (Object entity : entitys) {
            getSession().delete(entity);
            //getSession().flush();
        }
    }

    /**
     * 根据Id获取对象。
     */
    public <T> T get(Class<T> entityClass, final Serializable id) {
        return (T) getSession().get(entityClass, id);
    }

    /**
     * 根据主键获取实体
     *
     * @param entityName
     * @param id
     * @param <T>
     * @return
     */
    public <T> T getEntity(Class entityName, Serializable id) {

        T t = (T) getSession().get(entityName, id);
        if (t != null) {
            //getSession().flush();
        }
        return t;
    }

    /**
     * 更新指定的实体
     *
     * @param <T>
     * @param pojo
     */
    public <T> void updateEntitie(T pojo) {
        getSession().update(pojo);
        //getSession().flush();
    }

    /**
     * 更新指定的实体
     *
     * @param className
     * @param id
     * @param <T>
     */
    public <T> void updateEntitie(String className, Object id) {
        getSession().update(className, id);
        //getSession().flush();
    }

    /**
     * 根据主键更新实体
     */
    public <T> void updateEntityById(Class entityName, Serializable id) {
        updateEntitie(get(entityName, id));
    }

    /**
     * 通过hql 查询语句查找对象
     *
     * @param query
     * @return
     */
    public List<T> findByQueryString(final String query) {

        Query queryObject = getSession().createQuery(query);
        List<T> list = queryObject.list();
        //		if (list.size() > 0) {
        //getSession().flush();
        //		}
        return list;
    }

    /**
     * 通过hql查询唯一对象
     *
     * @param hql
     * @param <T>
     * @return
     */
    public <T> T singleResult(String hql) {
        T t = null;
        Query queryObject = getSession().createQuery(hql);
        List<T> list = queryObject.list();
        if (list.size() == 1) {
            //getSession().flush();
            t = list.get(0);
        } else if (list.size() > 0) {
            throw new RuntimeException("查询结果数:" + list.size() + "大于1");
        }
        return t;
    }

    /**
     * 通过hql 查询语句查找HashMap对象
     *
     * @param hql
     * @return
     */
    public Map<Object, Object> getHashMapbyQuery(String hql) {
        Query query = getSession().createQuery(hql);
        List list = query.list();
        Map<Object, Object> map = new HashMap<Object, Object>();
        for (Iterator iterator = list.iterator(); iterator.hasNext(); ) {
            Object[] tm = (Object[]) iterator.next();
            map.put(tm[0].toString(), tm[1].toString());
        }
        return map;
    }

    /**
     * 通过sql更新记录
     *
     * @param query
     * @return
     */
    public int updateBySqlString(final String query) {

        Query querys = getSession().createSQLQuery(query);
        return querys.executeUpdate();
    }

    /**
     * 通过sql查询语句查找对象
     *
     * @param sql
     * @return
     */
    public List<T> findListbySql(final String sql) {
        Query querys = getSession().createSQLQuery(sql);
        return querys.list();
    }

    /**
     * 创建Criteria对象，有排序功能。
     *
     * @param entityClass
     * @param isAsc
     * @param criterions
     * @param <T>
     * @return
     */
    private <T> Criteria createCriteria(
            Class<T> entityClass, boolean isAsc, Criterion... criterions) {
        Criteria criteria = createCriteria(entityClass, criterions);
        if (isAsc) {
            criteria.addOrder(Order.asc("asc"));
        } else {
            criteria.addOrder(Order.desc("desc"));
        }
        return criteria;
    }

    /**
     * 创建Criteria对象带属性比较
     *
     * @param <T>
     * @param entityClass
     * @param criterions
     * @return
     */
    private <T> Criteria createCriteria(Class<T> entityClass, Criterion... criterions) {
        Criteria criteria = getSession().createCriteria(entityClass);
        for (Criterion c : criterions) {
            criteria.add(c);
        }
        return criteria;
    }

    /**
     * 加载所有实体
     *
     * @param entityClass
     * @param <T>
     * @return
     */
    public <T> List<T> loadAll(final Class<T> entityClass) {
        Criteria criteria = createCriteria(entityClass);
        return criteria.list();
    }

    /**
     * 创建单一Criteria对象
     *
     * @param entityClass
     * @param <T>
     * @return
     */
    private <T> Criteria createCriteria(Class<T> entityClass) {
        Criteria criteria = getSession().createCriteria(entityClass);
        return criteria;
    }

    /**
     * 根据属性名和属性值查询. 有排序
     *
     * @param entityClass
     * @param propertyName
     * @param value
     * @param isAsc
     * @param <T>
     * @return
     */
    public <T> List<T> findByPropertyisOrder(
            Class<T> entityClass, String propertyName, Object value, boolean isAsc) {
        Assert.hasText(propertyName);
        return createCriteria(entityClass, isAsc, Restrictions.eq(propertyName, value)).list();
    }

    /**
     * 根据属性名和属性值 查询 且要求对象唯一.
     *
     * @return 符合条件的唯一对象.
     */
    public <T> T findUniqueBy(Class<T> entityClass, String propertyName, Object value) {
        Assert.hasText(propertyName);
        return (T) createCriteria(entityClass, Restrictions.eq(propertyName, value)).uniqueResult();
    }

    /**
     * 根据查询条件与参数列表创建Query对象
     *
     * @param session Hibernate会话
     * @param hql     HQL语句
     * @param objects 参数列表
     * @return Query对象
     */
    public Query createQuery(Session session, String hql, Object... objects) {
        Query query = session.createQuery(hql);
        if (objects != null) {
            for (int i = 0; i < objects.length; i++) {
                query.setParameter(i, objects[i]);
            }
        }
        return query;
    }

    /**
     * 批量插入实体
     *
     * @param entityList
     * @param <T>
     * @return
     */
    public <T> int batchInsertsEntitie(List<T> entityList) {
        int num = 0;
        for (int i = 0; i < entityList.size(); i++) {
            save(entityList.get(i));
            num++;
        }
        return num;
    }

    /**
     * 使用占位符的方式填充值 请注意：like对应的值格式："%"+username+"%" Hibernate Query
     *
     * @param hql
     * @param values 占位符号?对应的值，顺序必须一一对应 可以为空对象数组，但是不能为null
     * @return 2008-07-19 add by liuyang
     */
    public List<T> executeQuery(final String hql, final Object[] values) {
        Query query = getSession().createQuery(hql);
        // query.setCacheable(true);
        for (int i = 0; values != null && i < values.length; i++) {
            query.setParameter(i, values[i]);
        }

        return query.list();
    }

    /**
     * 根据实体模版查找
     *
     * @param entityName
     * @param exampleEntity
     * @return
     */
    public List findByExample(final String entityName, final Object exampleEntity) {
        Assert.notNull(exampleEntity, "Example bean must not be null");
        Criteria executableCriteria =
                (entityName != null
                        ? getSession().createCriteria(entityName)
                        : getSession().createCriteria(exampleEntity.getClass()));
        executableCriteria.add(Example.create(exampleEntity));
        return executableCriteria.list();
    }

    // 使用指定的检索标准获取满足标准的记录数
    public Integer getRowCount(DetachedCriteria criteria) {
        return OConvertUtils.getInt(
                ((Criteria) criteria.setProjection(Projections.rowCount())).uniqueResult(), 0);
    }

    /**
     * 查询指定实体的总记录数
     *
     * @param clazz
     * @return
     */
    public int getCount(Class<T> clazz) {

        int count =
                DataAccessUtils.intResult(
                        getSession().createQuery("select count(*) from " + clazz.getName()).list());
        return count;
    }

    /**
     * 获取分页记录CriteriaQuery 老方法final int allCounts = OConvertUtils.getInt(criteria
     * .setProjection(Projections.rowCount()).uniqueResult(), 0);
     *
     * @param cq
     * @param isOffset
     * @return
     */
    public Page getPageList(final CriteriaQuery cq, final boolean isOffset) {
        Criteria criteria = cq.getDetachedCriteria().getExecutableCriteria(getSession());
        CriteriaImpl impl = (CriteriaImpl) criteria;
        // 先把Projection和OrderBy条件取出来,清空两者来执行Count操作
        Projection projection = impl.getProjection();
        final int allCounts = ((Long) criteria.setProjection(Projections.rowCount()).uniqueResult()).intValue();
        criteria.setProjection(projection);
        if (projection == null) {
            criteria.setResultTransformer(CriteriaSpecification.ROOT_ENTITY);
        }

        // 判断是否有排序字段
        if (cq.getOrdermap() != null) {
            cq.setOrder(cq.getOrdermap());
        }
        int pageSize = cq.getPageSize(); // 每页显示数
        int curPageNO = PagerUtil.getcurPageNo(allCounts, cq.getCurPage(), pageSize); // 当前页
        int offset = PagerUtil.getOffset(allCounts, curPageNO, pageSize);
        if (isOffset) { // 是否分页
            criteria.setFirstResult(offset);
            criteria.setMaxResults(cq.getPageSize());
        } else {
            pageSize = allCounts;
        }
        return new Page(criteria.list(), pageSize, curPageNO, allCounts);
    }

    /**
     * 获取分页记录HqlQuery
     *
     * @param hqlQuery
     * @param needParameter
     * @return
     */
    @SuppressWarnings("unchecked")
    public Page getPageList(final HqlQuery hqlQuery, final boolean needParameter) {

        Query query = getSession().createQuery(hqlQuery.getQueryString());
        if (needParameter) {
            query.setParameters(hqlQuery.getParam(), (Type[]) hqlQuery.getTypes());
        }
        int allCounts = query.list().size();
        int curPageNO = hqlQuery.getCurPage();
        int offset = PagerUtil.getOffset(allCounts, curPageNO, hqlQuery.getPageSize());
        query.setFirstResult(offset);
        query.setMaxResults(hqlQuery.getPageSize());
        return new Page(query.list(), offset, curPageNO, allCounts);
    }

    /**
     * 根据CriteriaQuery获取List
     *
     * @param cq
     * @param ispage
     * @return
     */
    @SuppressWarnings("unchecked")
    public List<T> getListByCriteriaQuery(final CriteriaQuery cq, Boolean ispage) {
        Criteria criteria = cq.getDetachedCriteria().getExecutableCriteria(getSession());
        // 判断是否有排序字段
        if (cq.getOrdermap() != null) {
            cq.setOrder(cq.getOrdermap());
        }
        if (ispage) {
            criteria.setFirstResult((cq.getCurPage() - 1) * cq.getPageSize());
            criteria.setMaxResults(cq.getPageSize());
        }
        return criteria.list();
    }

    @Autowired
    @Qualifier("jdbcTemplate")
    private JdbcTemplate jdbcTemplate;

    @Autowired
    @Qualifier("namedParameterJdbcTemplate")
    private NamedParameterJdbcTemplate namedParameterJdbcTemplate;

    /**
     * 使用指定的检索标准检索数据并分页返回数据For JDBC
     */
    public Long getCountForJdbc(String sql) {
        return this.jdbcTemplate.queryForObject(sql, Long.class);
    }

    /**
     * 使用指定的检索标准检索数据并分页返回数据For JDBC-采用预处理方式
     */
    public Long getCountForJdbcParam(String sql, Object[] objs) {
        return this.jdbcTemplate.queryForObject(sql, objs, Long.class);
    }

    /**
     * jdbc方式查找
     *
     * @param sql
     * @param objs
     * @return
     */
    public List<Map<String, Object>> findForJdbc(String sql, Object... objs) {
        return this.jdbcTemplate.queryForList(sql, objs);
    }

    /**
     * 执行SQL
     *
     * @param sql
     * @param param
     * @return
     */
    public Integer executeSql(String sql, List<Object> param) {
        return this.jdbcTemplate.update(sql, param);
    }

    /**
     * 执行SQL
     *
     * @param sql
     * @param param
     * @return
     */
    public Integer executeSql(String sql, Object... param) {
        return this.jdbcTemplate.update(sql, param);
    }

    /**
     * 执行SQL
     *
     * @param sql
     * @param param
     * @return
     */
    public Integer executeSql(String sql, Map<String, Object> param) {
        return this.namedParameterJdbcTemplate.update(sql, param);
    }

    /**
     * 执行SQL
     *
     * @param sql
     * @param param
     * @return
     */
    public Object executeSqlReturnKey(final String sql, Map<String, Object> param) {
        Object keyValue = null;
        KeyHolder keyHolder = null;
        SqlParameterSource sqlp = new MapSqlParameterSource(param);

        if (StringUtil.isNotEmpty(param.get("id"))) { //表示已经生成过id(UUID),则表示是非序列或数据库自增的形式
            this.namedParameterJdbcTemplate.update(sql, sqlp);
        } else { //NATIVE or SEQUENCE
            keyHolder = new GeneratedKeyHolder();
            this.namedParameterJdbcTemplate.update(sql, sqlp, keyHolder, new String[]{"id"});
            Number number = keyHolder.getKey();
            if (OConvertUtils.isNotEmpty(number)) {
                keyValue = keyHolder.getKey().longValue();
            }
        }

        return keyValue;
    }

    /**
     * jdbc方式获取个数
     *
     * @param sql
     * @param param
     * @return
     */
    public Integer countByJdbc(String sql, Object... param) {
        return this.jdbcTemplate.queryForObject(sql, param, Integer.class);
    }

    public Map<String, Object> findOneForJdbc(String sql, Object... objs) {
        try {
            return this.jdbcTemplate.queryForMap(sql, objs);
        } catch (EmptyResultDataAccessException e) {
            return null;
        }
    }

    /**
     * 通过hql查询语句查找对象
     *
     * @param hql
     * @param param
     * @param <T>
     * @return
     */
    public <T> List<T> findHql(String hql, Object... param) {
        Query q = getSession().createQuery(hql);
        if (param != null && param.length > 0) {
            for (int i = 0; i < param.length; i++) {
                q.setParameter(i, param[i]);
            }
        }
        return q.list();
    }

    /**
     * 执行HQL语句操作更新
     *
     * @param hql
     * @return
     */
    public Integer executeHql(String hql) {
        Query q = getSession().createQuery(hql);
        return q.executeUpdate();
    }

    public <T> List<T> pageList(DetachedCriteria dc, int firstResult, int maxResult) {
        Criteria criteria = dc.getExecutableCriteria(getSession());
        criteria.setResultTransformer(CriteriaSpecification.ROOT_ENTITY);
        criteria.setFirstResult(firstResult);
        criteria.setMaxResults(maxResult);
        return criteria.list();
    }

    /**
     * 离线查询
     */
    public <T> List<T> findByDetached(DetachedCriteria dc) {
        return dc.getExecutableCriteria(getSession()).list();
    }

    /**
     * 调用存储过程
     */
    @SuppressWarnings({
            "unchecked",
    })
    public <T> List<T> executeProcedure(String executeSql, Object... params) {
        SQLQuery sqlQuery = getSession().createSQLQuery(executeSql);

        for (int i = 0; i < params.length; i++) {
            sqlQuery.setParameter(i, params[i]);
        }

        return sqlQuery.list();
    }


    public <T> List<T> findListMapBySql(String sql) {
        Query sqlQuery = getSession().createSQLQuery(sql).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
//        List<Map> listMap= sqlQuery.list();
        return sqlQuery.list();
    }

}
